1   /*
2    * Copyright (C) 2011 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.hash;
16  
17  import com.google.common.annotations.Beta;
18  import com.google.common.base.Preconditions;
19  
20  import java.io.OutputStream;
21  import java.io.Serializable;
22  import java.nio.charset.Charset;
23  
24  import javax.annotation.Nullable;
25  
26  /**
27   * Funnels for common types. All implementations are serializable.
28   *
29   * @author Dimitris Andreou
30   * @since 11.0
31   */
32  @Beta
33  public final class Funnels {
34    private Funnels() {}
35  
36    /**
37     * Returns a funnel that extracts the bytes from a {@code byte} array.
38     */
39    public static Funnel<byte[]> byteArrayFunnel() {
40      return ByteArrayFunnel.INSTANCE;
41    }
42  
43    private enum ByteArrayFunnel implements Funnel<byte[]> {
44      INSTANCE;
45  
46      public void funnel(byte[] from, PrimitiveSink into) {
47        into.putBytes(from);
48      }
49  
50      @Override public String toString() {
51        return "Funnels.byteArrayFunnel()";
52      }
53    }
54  
55    /**
56     * Returns a funnel that extracts the characters from a {@code CharSequence}, a character at a
57     * time, without performing any encoding. If you need to use a specific encoding, use
58     * {@link Funnels#stringFunnel(Charset)} instead.
59     *
60     * @since 15.0 (since 11.0 as {@code Funnels.stringFunnel()}.
61     */
62    public static Funnel<CharSequence> unencodedCharsFunnel() {
63      return UnencodedCharsFunnel.INSTANCE;
64    }
65  
66    private enum UnencodedCharsFunnel implements Funnel<CharSequence> {
67      INSTANCE;
68  
69      public void funnel(CharSequence from, PrimitiveSink into) {
70        into.putUnencodedChars(from);
71      }
72  
73      @Override public String toString() {
74        return "Funnels.unencodedCharsFunnel()";
75      }
76    }
77  
78    /**
79     * Returns a funnel that encodes the characters of a {@code CharSequence} with the specified
80     * {@code Charset}.
81     *
82     * @since 15.0
83     */
84    public static Funnel<CharSequence> stringFunnel(Charset charset) {
85      return new StringCharsetFunnel(charset);
86    }
87  
88    private static class StringCharsetFunnel implements Funnel<CharSequence>, Serializable {
89      private final Charset charset;
90  
91      StringCharsetFunnel(Charset charset) {
92        this.charset = Preconditions.checkNotNull(charset);
93      }
94  
95      public void funnel(CharSequence from, PrimitiveSink into) {
96        into.putString(from, charset);
97      }
98  
99      @Override public String toString() {
100       return "Funnels.stringFunnel(" + charset.name() + ")";
101     }
102 
103     @Override public boolean equals(@Nullable Object o) {
104       if (o instanceof StringCharsetFunnel) {
105         StringCharsetFunnel funnel = (StringCharsetFunnel) o;
106         return this.charset.equals(funnel.charset);
107       }
108       return false;
109     }
110 
111     @Override public int hashCode() {
112       return StringCharsetFunnel.class.hashCode() ^ charset.hashCode();
113     }
114 
115     Object writeReplace() {
116       return new SerializedForm(charset);
117     }
118 
119     private static class SerializedForm implements Serializable {
120       private final String charsetCanonicalName;
121 
122       SerializedForm(Charset charset) {
123         this.charsetCanonicalName = charset.name();
124       }
125 
126       private Object readResolve() {
127         return stringFunnel(Charset.forName(charsetCanonicalName));
128       }
129     
130       private static final long serialVersionUID = 0;
131     }
132   }
133 
134   /**
135    * Returns a funnel for integers.
136    *
137    * @since 13.0
138    */
139   public static Funnel<Integer> integerFunnel() {
140     return IntegerFunnel.INSTANCE;
141   }
142 
143   private enum IntegerFunnel implements Funnel<Integer> {
144     INSTANCE;
145 
146     public void funnel(Integer from, PrimitiveSink into) {
147       into.putInt(from);
148     }
149 
150     @Override public String toString() {
151       return "Funnels.integerFunnel()";
152     }
153   }
154 
155   /**
156    * Returns a funnel that processes an {@code Iterable} by funneling its elements in iteration
157    * order with the specified funnel.  No separators are added between the elements.
158    *
159    * @since 15.0
160    */
161   public static <E> Funnel<Iterable<? extends E>> sequentialFunnel(Funnel<E> elementFunnel) {
162     return new SequentialFunnel<E>(elementFunnel);
163   }
164 
165   private static class SequentialFunnel<E> implements Funnel<Iterable<? extends E>>, Serializable {
166     private final Funnel<E> elementFunnel;
167 
168     SequentialFunnel(Funnel<E> elementFunnel) {
169       this.elementFunnel = Preconditions.checkNotNull(elementFunnel);
170     }
171 
172     public void funnel(Iterable<? extends E> from, PrimitiveSink into) {
173       for (E e : from) {
174         elementFunnel.funnel(e, into);
175       }
176     }
177 
178     @Override public String toString() {
179       return "Funnels.sequentialFunnel(" + elementFunnel + ")";
180     }
181 
182     @Override public boolean equals(@Nullable Object o) {
183       if (o instanceof SequentialFunnel) {
184         SequentialFunnel<?> funnel = (SequentialFunnel<?>) o;
185         return elementFunnel.equals(funnel.elementFunnel);
186       }
187       return false;
188     }
189 
190     @Override public int hashCode() {
191       return SequentialFunnel.class.hashCode() ^ elementFunnel.hashCode();
192     }
193   }
194 
195   /**
196    * Returns a funnel for longs.
197    * 
198    * @since 13.0
199    */
200   public static Funnel<Long> longFunnel() {
201     return LongFunnel.INSTANCE;
202   }
203   
204   private enum LongFunnel implements Funnel<Long> {
205     INSTANCE;
206     
207     public void funnel(Long from, PrimitiveSink into) {
208       into.putLong(from);
209     }
210     
211     @Override public String toString() {
212       return "Funnels.longFunnel()";
213     }
214   }
215   
216   /**
217    * Wraps a {@code PrimitiveSink} as an {@link OutputStream}, so it is easy to
218    * {@link Funnel#funnel funnel} an object to a {@code PrimitiveSink}
219    * if there is already a way to write the contents of the object to an {@code OutputStream}.  
220    * 
221    * <p>The {@code close} and {@code flush} methods of the returned {@code OutputStream}
222    * do nothing, and no method throws {@code IOException}.
223    * 
224    * @since 13.0
225    */
226   public static OutputStream asOutputStream(PrimitiveSink sink) {
227     return new SinkAsStream(sink);
228   }
229   
230   private static class SinkAsStream extends OutputStream {
231     final PrimitiveSink sink;
232     SinkAsStream(PrimitiveSink sink) {
233       this.sink = Preconditions.checkNotNull(sink);
234     }
235     
236     @Override public void write(int b) {
237       sink.putByte((byte) b);
238     }
239 
240     @Override public void write(byte[] bytes) {
241       sink.putBytes(bytes);
242     }
243 
244     @Override public void write(byte[] bytes, int off, int len) {
245       sink.putBytes(bytes, off, len);
246     }
247     
248     @Override public String toString() {
249       return "Funnels.asOutputStream(" + sink + ")";
250     }
251   }
252 }